package com.meilai.project.service.business.intentionScheme;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilai.project.controller.mini.dto.MiniCustomerNeedSearchParams;
import com.meilai.project.controller.mini.vo.MiniCustomerNeedVO;
import com.meilai.project.core.excel.ExcelGenerator;
import com.meilai.project.core.excel.ReportSheet;
import com.meilai.project.core.excel.config.TableHeaderConfig;
import com.meilai.project.dto.base.ApiResult;
import com.meilai.project.dto.business.intentionScheme.*;
import com.meilai.project.entity.business.intentionScheme.IntentionScheme;
import com.meilai.project.entity.business.sale.BillSale;
import com.meilai.project.exception.CommonException;
import com.meilai.project.mapper.intentionScheme.IntentionSchemeMapper;
import com.meilai.project.service.business.contract.ContractApplyService;
import com.meilai.project.service.business.customer.CustomerNeedService;
import com.meilai.project.service.business.sale.BillSaleService;
import com.meilai.project.service.system.MessageCenterService;
import com.meilai.project.util.ThreadLocalUserUtil;
import com.meilai.project.vo.business.intentionScheme.DateRangeMediaTypeVO;
import com.meilai.project.vo.business.intentionScheme.IntentionSchemeVO;
import com.meilai.project.vo.business.intentionScheme.PointProcessDateRangeVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author xchen
 * @since 2022-02-23
 */
@Service
public class IntentionSchemeServiceImpl extends ServiceImpl<IntentionSchemeMapper, IntentionScheme> implements IntentionSchemeService {

    @Autowired
    private IntentionSchemeMapper intentionSchemeMapper;

    @Autowired
    private PointProcessService processService;

    @Autowired
    private MessageCenterService messageCenterService;

    @Autowired
    private ContractApplyService contractApplyService;

    @Autowired
    private BillSaleService billSaleService;

    @Autowired
    private CustomerNeedService customerNeedService;


    @Override
    public void updateContractId(Long oldContractId, Long newContractId) {
        baseMapper.updateContractId(oldContractId, newContractId);
    }

    @Override
    public IntentionScheme queryByChooseId(Long chooseId) {
        return baseMapper.queryByChooseId(chooseId);
    }

    @Override
    public List<IntentionSchemeVO> listByRelated(Long related_id, Integer related_type, Long must_include_bill_sale_id, Integer type) {
        List<IntentionSchemeVO> intentionSchemeVOS = baseMapper.listByRelated(related_id, related_type, type);
        if (must_include_bill_sale_id != null) {
            IntentionSchemeVO vo = baseMapper.getOneById(must_include_bill_sale_id);
            if (vo != null) {
                intentionSchemeVOS.add(vo);
            }
        }
        return intentionSchemeVOS;
    }

    @Override
    public List<IntentionSchemeVO> listAllByRelatedType(Integer related_type) {
        return baseMapper.listAllByRelatedType(related_type);
    }

    @Override
    public List<BuildingPointVO> search(PointProcessSearchDTO searchDTO) {
        return processService.search(searchDTO);
    }

    @Override
    public List<BuildingPointVO> searchAll(PointProcessSearchDTO searchDTO) {
        return processService.searchAll(searchDTO);
    }

    @Override
    public Page<IntentionSchemeVO> selectPageList(IntentionSchemeListDTO searchDTO) {
        Page<IntentionSchemeVO> page = searchDTO.createPage(IntentionSchemeVO.class);
        List<IntentionSchemeVO> intentionSchemeVOS = intentionSchemeMapper.listByCondition(page, searchDTO);
        page.setRecords(intentionSchemeVOS);
        if (CollUtil.isNotEmpty(intentionSchemeVOS)) {
            List<Long> ids = intentionSchemeVOS.stream().map(IntentionSchemeVO::getId).collect(Collectors.toList());
            LambdaQueryWrapper<BillSale> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.isNull(BillSale::getDeleted_at).ne(BillSale::getMode, 2)
                    .inSql(BillSale::getIntention_scheme_id, StrUtil.join(",", ids));
            List<BillSale> billSaleList = billSaleService.list(lambdaQueryWrapper);
            Set<Long> containSet = billSaleList.stream().map(BillSale::getIntention_scheme_id).collect(Collectors.toSet());
            // todo 增加媒体类型数据
            List<Long> choose_ids = intentionSchemeVOS.stream().map(IntentionSchemeVO::getChoose_id).collect(Collectors.toList());
            Map<Long, List<DateRangeMediaTypeVO>> map = baseMapper.queryMediaTypeListByDateRangeIds(choose_ids).stream().collect(Collectors.groupingBy(DateRangeMediaTypeVO::getChoose_id));
            for (IntentionSchemeVO intentionSchemeVO : intentionSchemeVOS) {
                if (containSet.contains(intentionSchemeVO.getId())) {
                    intentionSchemeVO.setHasBillSale(true);
                }
                if (map.containsKey(intentionSchemeVO.getChoose_id())) {
                    Set<String> collect = map.get(intentionSchemeVO.getChoose_id()).stream().map(DateRangeMediaTypeVO::getMedia_type_name).collect(Collectors.toSet());
                    intentionSchemeVO.setMedia_type_name_list(new ArrayList<>(collect));
                } else {
                    intentionSchemeVO.setMedia_type_name_list(new ArrayList<>());
                }
            }
        }
        return page;
    }

    @Override
    public List<IntentionSchemeVO> listByContractId(Long contractId) {
        return baseMapper.listByContractId(contractId);
    }

    @Override
    public void exportExcel(HttpServletResponse response, IntentionSchemeListDTO searchDTO) {
        ReportSheet reportSheet = ReportSheet.create().addTableHeaderConfig(tableHeaderConfigList());
        ExcelGenerator generator = ExcelGenerator.createExcel().sheet(reportSheet);
        int currentPage = 1;
        Page<IntentionSchemeVO> page = searchDTO.createPage(IntentionSchemeVO.class);
        page.setSize(1000);
        page.setCurrent(currentPage);
        List<IntentionSchemeVO> intentionSchemeVOS = intentionSchemeMapper.listByCondition(page, searchDTO);
        //写入Excel
        generator.write(intentionSchemeVOS);
        while (page.hasNext()) {
            currentPage++;
            page.setCurrent(currentPage);
            intentionSchemeVOS = intentionSchemeMapper.listByCondition(page, searchDTO);
            //写入Excel
            generator.write(intentionSchemeVOS);
        }
        generator.save(response, "意向方案列表");
    }

    public List<TableHeaderConfig> tableHeaderConfigList() {
        List<TableHeaderConfig> tableHeaderConfigList = new ArrayList<>();
        tableHeaderConfigList.add(TableHeaderConfig.of("锁定状态", "process_status", (cellValue, rowValue) -> {
            Integer process_status = (Integer) cellValue;
            if (1 == process_status) {
                return "未锁定";
            } else {
                return "锁定";
            }
        }));
        tableHeaderConfigList.add(TableHeaderConfig.of("释放/锁定", "lock_count", (cellValue, rowData) -> {
            Integer process_status = (Integer) BeanUtil.getFieldValue(rowData, "process_status");
            Integer lock_count = (Integer) cellValue;
            if (1 == process_status) {
                return lock_count + "/0";
            } else {
                return "0/" + lock_count;
            }
        }));
        tableHeaderConfigList.add(TableHeaderConfig.of("方案名称", "plan_name"));
        tableHeaderConfigList.add(TableHeaderConfig.of("方案类型", "plan_type_name"));
        tableHeaderConfigList.add(TableHeaderConfig.of("锁定周期", "cycle_date"));
        tableHeaderConfigList.add(TableHeaderConfig.of("锁定人", "lockUserName"));
        tableHeaderConfigList.add(TableHeaderConfig.of("锁定时间", "lock_datetime", (cellValue, rowData) -> {
            LocalDateTime localDateTime = (LocalDateTime) cellValue;
            if (null == localDateTime) {
                return "";
            }
            return localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }));
        return tableHeaderConfigList;
    }

    @Override
    public IntentionSchemeVO getOneById(Long id) {
        IntentionSchemeVO result = baseMapper.queryById(id);
        result.setHasBillSale(billSaleService.list(Wrappers.<BillSale>lambdaQuery().eq(BillSale::getIntention_scheme_id, id).isNull(BillSale::getDeleted_at).ne(BillSale::getMode, 2)).size() > 0);
        List<PointProcessDateRangeVO> pointProcessDateRangeVOList =
                processService.queryChooseDateRangeVOList(result.getChoose_id());
        result.setDateRangeVOList(pointProcessDateRangeVOList);
        if (pointProcessDateRangeVOList.size() > 0) {
            result.setDaterange_id(pointProcessDateRangeVOList.get(0).getId());
        }
        return result;
    }

    @Override
    public IntentionScheme getByBillSaleId(Long billSaleId) {
        return intentionSchemeMapper.getByBillSaleId(billSaleId);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public ApiResult<Boolean> linkContract(IntentionSchemeLinkContractDTO dto) {
        return ApiResult.ok(update(Wrappers.<IntentionScheme>lambdaUpdate()
                .set(IntentionScheme::getRelated_id, dto.getContract_id())
                .set(IntentionScheme::getRelated_contract_id, dto.getContract_id())
                .eq(IntentionScheme::getId, dto.getId())));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<Boolean> unlinkContract(Long id) {

        boolean hasBillSale = billSaleService.list(Wrappers.<BillSale>lambdaQuery().eq(BillSale::getIntention_scheme_id, id)
                .isNull(BillSale::getDeleted_at).ne(BillSale::getMode, 2)).size() > 0;
        if(hasBillSale) throw new CommonException("存在关联的销售单，无法解除合同");

        return ApiResult.ok(update(Wrappers.<IntentionScheme>lambdaUpdate()
                .set(IntentionScheme::getRelated_id, null)
                .set(IntentionScheme::getRelated_contract_id, null)
                .eq(IntentionScheme::getId, id)));
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public ApiResult<IntentionSchemeSaveDTO> save(IntentionSchemeSaveDTO dto) {
        //保存选择的点位信息
        Long choose_id = processService.saveChoose(1, dto.getChoose_id(), dto.getDataList());
        saveScheme(dto, choose_id, 1);

        return ApiResult.ok(dto);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public ApiResult<IntentionSchemeSaveDTO> lock(IntentionSchemeSaveDTO dto) {
        List<PointProcessDateRangeDTO> dataList = dto.getDataList();
        PointProcessDateRangeLockedDTO lockedDTO =
                processService.checkRangeDateLockedPointList(dataList);
        if (null != lockedDTO) {
            //存在锁定的点
            return ApiResult.ok(lockedDTO);
        }

        PointProcessLockedMediaPointDTO lockedMediaPointDTO;
        //当关联类型为销售单时，需要去除sale的占用点位再校验
        if (null != dto.getRelated_type() && dto.getRelated_type() == 2) {
            List<Long> chooseIdList = getAllChooseIdList(dto.getChoose_id(), dto.getRelated_id());
            lockedMediaPointDTO = processService.checkOccupyMediaPointList(chooseIdList, dataList);
            //备份并删除对应销售单上的占用
            if (null == lockedMediaPointDTO) {
                if (dto.getChoose_id() != null) {
                    chooseIdList.remove(0);
                }

                processService.releaseLockAndBackUp(chooseIdList);
                //取消挂起合同、销售单
                processService.hangUpByChooseIds(chooseIdList);
            }
        } else {
            lockedMediaPointDTO = processService.checkOccupyMediaPointList(dto.getChoose_id(), dataList);
        }

        if (null != lockedMediaPointDTO) {
            return ApiResult.ok(lockedMediaPointDTO);
        }

        //保存选择的点位信息
        Long chooseId = processService.saveChoose(1, dto.getChoose_id(), dataList);
        saveScheme(dto, chooseId, 2);
        // todo 换画类型不锁定点位
        if (dto.getType() == 1) {
            processService.lock(chooseId);
        }
        return ApiResult.ok(dto);
    }

    private List<Long> getAllChooseIdList(Long chooseId, Long billSaleId) {
        List<Long> chooseIdList = new ArrayList<>();
        if (chooseId != null) {
            chooseIdList.add(chooseId);
        }
        Long saleChooseId = processService.getSaleChooseId(billSaleId);
        if (saleChooseId != null) {
            chooseIdList.add(saleChooseId);
        }
        return chooseIdList;
    }

    @Override
    public ApiResult<PointProcessLockedMediaPointDTO> checkDateRangeLock(IntentionSchemeSaveDTO dto) {
        List<PointProcessDateRangeDTO> dataList = dto.getDataList();
        if (CollUtil.isEmpty(dataList)) {
            return ApiResult.ok();
        }
        List<Long> chooseIdList = new ArrayList<>();
        Long choose_id = dto.getChoose_id();
        if (null != choose_id) {
            chooseIdList.add(choose_id);
        }
        if (dto.getExclude_sale_choose_id() != null) {
            chooseIdList.add(dto.getExclude_sale_choose_id());
        }
        PointProcessLockedMediaPointDTO lockedDTO = processService.checkOccupyMediaPointList(chooseIdList, dataList);
        if (null != lockedDTO) {
            //存在锁定的点
            return ApiResult.ok(lockedDTO);
        } else {
            return ApiResult.ok();
        }
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public Boolean release(Long id) {
        //释放锁定
        try {
            IntentionScheme intentionScheme = baseMapper.selectById(id);
            Long choose_id = intentionScheme.getChoose_id();
            //释放锁定
            processService.releaseLock(choose_id);
            //更新锁定信息
            intentionScheme.setLock_user_id(null);
            intentionScheme.setLock_datetime(null);
            intentionScheme.setProcess_status(1);
            updateById(intentionScheme);
            //如果删除了方案，则回滚对应的合同和销售单的占用
            if (intentionScheme.getRelated_type() == 2) {
                List<Long> allChooseIdList = getAllChooseIdList(null, intentionScheme.getRelated_id());
                processService.rollback(allChooseIdList);
                //解除合同和销售单的挂起状态
                processService.cancelHangUpByChooseIds(allChooseIdList);
            }
        } catch (Exception e) {
            throw new CommonException("释放失败,请联系管理员");
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public Boolean delete(Long id) {
        try {
            IntentionScheme intentionScheme = baseMapper.selectById(id);
            if (intentionScheme.getProcess_status() == 2) {
                throw new CommonException("请先释放锁定后再删除！");
            }
            intentionScheme.setStatus(0);
            updateById(intentionScheme);

            Long choose_id = intentionScheme.getChoose_id();
            //释放锁定
            processService.releaseLock(choose_id);
        } catch (Exception e) {
            throw new CommonException("删除失败！");
        }

        //删除方案
        return true;
    }


    public void saveScheme(IntentionSchemeSaveDTO dto, Long choose_id, int processStatus) {
        // 获取当前人的id和部门id
        IntentionScheme temp = new IntentionScheme();
        BeanUtils.copyProperties(dto, temp);

        // 是否为新数据
        // 新数据时，前端不传业务数据id
        // 非新数据时，即来源是“草稿箱”、“已删除”、“被退回”时，前端需要传入业务数据id
        boolean isFirst = (dto.getId() == null);

        // 由于流程字段含有id，会覆盖数据，此处需要手动setId
        if (!isFirst) {
            temp.setId(dto.getId());
        }

        temp.setChoose_id(choose_id);
        temp.setProcess_status(processStatus);
        temp.setLock_count(dto.getMediaPointCount());
        temp.setStatus(1);

        if (2 == processStatus) {
            Long currUserId = ThreadLocalUserUtil.get().getId();
            temp.setLock_user_id(currUserId);
            temp.setLock_datetime(LocalDateTime.now());
        }

        if (temp.getRelated_type() == 2) {
            IntentionScheme intentionScheme = getByBillSaleId(temp.getRelated_id());
            temp.setRelated_contract_id(intentionScheme.getRelated_contract_id());
        } else {
            temp.setRelated_contract_id(temp.getRelated_id());
        }

        // 新增或更新业务数据
        boolean flag = isFirst ? save(temp) : updateById(temp);
        if (!flag) throw new CommonException("保存失败");

        dto.setId(temp.getId());
    }

    @Override
    @Transactional
    public Boolean pushMSG(Long id) {
        IntentionSchemeVO data = baseMapper.getPushMsgData(id);
        if (data != null) {
            String title = "《" + data.getPlan_name() + "》锁位完成";
            String sb = "项目：" + data.getCustomer_name() + "<br/>" +
                    "时间段：" + data.getCycle_date() + "<br/>" +
                    "锁定数量：" + data.getLock_count() + "<br/>" +
                    "锁定人：" + data.getLockUserName();
            messageCenterService.genPushMessage(data.getMaintenance_staff_id(), title, sb, "contract_my", null);
            this.update(Wrappers.<IntentionScheme>lambdaUpdate().set(IntentionScheme::getIs_push, 1).eq(IntentionScheme::getId, id));
        } else {
            throw new CommonException("锁定方案不存在!请联系管理员");
        }
        return true;
    }

    @Override
    public Page<MiniCustomerNeedVO> needSelectPage(MiniCustomerNeedSearchParams params) {
        return customerNeedService.selectPageList(params);
    }
}
